/*
 * PLyResult 类
 *
 * src/pl/plpython/plpy_resultobject.c
 */

#include "postgres.h"

#include "plpy_elog.h"
#include "plpy_resultobject.h"
#include "plpython.h"

static void fc_PLy_result_dealloc(PyObject *fc_arg);
static PyObject *fc_PLy_result_colnames(PyObject *fc_self, PyObject *fc_unused);
static PyObject *fc_PLy_result_coltypes(PyObject *fc_self, PyObject *fc_unused);
static PyObject *fc_PLy_result_coltypmods(PyObject *fc_self, PyObject *fc_unused);
static PyObject *fc_PLy_result_nrows(PyObject *fc_self, PyObject *fc_args);
static PyObject *fc_PLy_result_status(PyObject *fc_self, PyObject *fc_args);
static Py_ssize_t fc_PLy_result_length(PyObject *fc_arg);
static PyObject *fc_PLy_result_item(PyObject *fc_arg, Py_ssize_t fc_idx);
static PyObject *fc_PLy_result_str(PyObject *fc_arg);
static PyObject *fc_PLy_result_subscript(PyObject *fc_arg, PyObject *fc_item);
static int	fc_PLy_result_ass_subscript(PyObject *fc_self, PyObject *fc_item, PyObject *fc_value);

static char PLy_result_doc[] = "Results of a PostgreSQL query";

static PySequenceMethods PLy_result_as_sequence = {
	.sq_length = fc_PLy_result_length,
	.sq_item = fc_PLy_result_item,
};

static PyMappingMethods PLy_result_as_mapping = {
	.mp_length = fc_PLy_result_length,
	.mp_subscript = fc_PLy_result_subscript,
	.mp_ass_subscript = fc_PLy_result_ass_subscript,
};

static PyMethodDef PLy_result_methods[] = {
	{"colnames", fc_PLy_result_colnames, METH_NOARGS, NULL},
	{"coltypes", fc_PLy_result_coltypes, METH_NOARGS, NULL},
	{"coltypmods", fc_PLy_result_coltypmods, METH_NOARGS, NULL},
	{"nrows", fc_PLy_result_nrows, METH_VARARGS, NULL},
	{"status", fc_PLy_result_status, METH_VARARGS, NULL},
	{NULL, NULL, 0, NULL}
};

static PyTypeObject PLy_ResultType = {
	PyVarObject_HEAD_INIT(NULL, 0)
	.tp_name = "PLyResult",
	.tp_basicsize = sizeof(PLyResultObject),
	.tp_dealloc = fc_PLy_result_dealloc,
	.tp_as_sequence = &PLy_result_as_sequence,
	.tp_as_mapping = &PLy_result_as_mapping,
	.tp_str = &fc_PLy_result_str,
	.tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
	.tp_doc = PLy_result_doc,
	.tp_methods = PLy_result_methods,
};

void PLy_result_init_type(void)
{
	if (PyType_Ready(&PLy_ResultType) < 0)
		elog(ERROR, "could not initialize PLy_ResultType");
}

PyObject * PLy_result_new(void)
{
	PLyResultObject *fc_ob;

	if ((fc_ob = PyObject_New(PLyResultObject, &PLy_ResultType)) == NULL)
		return NULL;

	/* ob->tuples = NULL; */

	Py_INCREF(Py_None);
	fc_ob->status = Py_None;
	fc_ob->nrows = PyLong_FromLong(-1);
	fc_ob->rows = PyList_New(0);
	fc_ob->tupdesc = NULL;
	if (!fc_ob->rows)
	{
		Py_DECREF(fc_ob);
		return NULL;
	}

	return (PyObject *) fc_ob;
}

static void fc_PLy_result_dealloc(PyObject *fc_arg)
{
	PLyResultObject *fc_ob = (PLyResultObject *) fc_arg;

	Py_XDECREF(fc_ob->nrows);
	Py_XDECREF(fc_ob->rows);
	Py_XDECREF(fc_ob->status);
	if (fc_ob->tupdesc)
	{
		FreeTupleDesc(fc_ob->tupdesc);
		fc_ob->tupdesc = NULL;
	}

	fc_arg->ob_type->tp_free(fc_arg);
}

static PyObject * fc_PLy_result_colnames(PyObject *fc_self, PyObject *fc_unused)
{
	PLyResultObject *fc_ob = (PLyResultObject *) fc_self;
	PyObject   *fc_list;
	int			fc_i;

	if (!fc_ob->tupdesc)
	{
		PLy_exception_set(PLy_exc_error, "command did not produce a result set");
		return NULL;
	}

	fc_list = PyList_New(fc_ob->tupdesc->natts);
	if (!fc_list)
		return NULL;
	for (fc_i = 0; fc_i < fc_ob->tupdesc->natts; fc_i++)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_ob->tupdesc, fc_i);

		PyList_SET_ITEM(fc_list, fc_i, PLyUnicode_FromString(NameStr(fc_attr->attname)));
	}

	return fc_list;
}

static PyObject * fc_PLy_result_coltypes(PyObject *fc_self, PyObject *fc_unused)
{
	PLyResultObject *fc_ob = (PLyResultObject *) fc_self;
	PyObject   *fc_list;
	int			fc_i;

	if (!fc_ob->tupdesc)
	{
		PLy_exception_set(PLy_exc_error, "command did not produce a result set");
		return NULL;
	}

	fc_list = PyList_New(fc_ob->tupdesc->natts);
	if (!fc_list)
		return NULL;
	for (fc_i = 0; fc_i < fc_ob->tupdesc->natts; fc_i++)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_ob->tupdesc, fc_i);

		PyList_SET_ITEM(fc_list, fc_i, PyLong_FromLong(fc_attr->atttypid));
	}

	return fc_list;
}

static PyObject * fc_PLy_result_coltypmods(PyObject *fc_self, PyObject *fc_unused)
{
	PLyResultObject *fc_ob = (PLyResultObject *) fc_self;
	PyObject   *fc_list;
	int			fc_i;

	if (!fc_ob->tupdesc)
	{
		PLy_exception_set(PLy_exc_error, "command did not produce a result set");
		return NULL;
	}

	fc_list = PyList_New(fc_ob->tupdesc->natts);
	if (!fc_list)
		return NULL;
	for (fc_i = 0; fc_i < fc_ob->tupdesc->natts; fc_i++)
	{
		Form_pg_attribute fc_attr = TupleDescAttr(fc_ob->tupdesc, fc_i);

		PyList_SET_ITEM(fc_list, fc_i, PyLong_FromLong(fc_attr->atttypmod));
	}

	return fc_list;
}

static PyObject * fc_PLy_result_nrows(PyObject *fc_self, PyObject *fc_args)
{
	PLyResultObject *fc_ob = (PLyResultObject *) fc_self;

	Py_INCREF(fc_ob->nrows);
	return fc_ob->nrows;
}

static PyObject * fc_PLy_result_status(PyObject *fc_self, PyObject *fc_args)
{
	PLyResultObject *fc_ob = (PLyResultObject *) fc_self;

	Py_INCREF(fc_ob->status);
	return fc_ob->status;
}

static Py_ssize_t
fc_PLy_result_length(PyObject *fc_arg)
{
	PLyResultObject *fc_ob = (PLyResultObject *) fc_arg;

	return PyList_Size(fc_ob->rows);
}

static PyObject * fc_PLy_result_item(PyObject *fc_arg, Py_ssize_t fc_idx)
{
	PyObject   *fc_rv;
	PLyResultObject *fc_ob = (PLyResultObject *) fc_arg;

	fc_rv = PyList_GetItem(fc_ob->rows, fc_idx);
	if (fc_rv != NULL)
		Py_INCREF(fc_rv);
	return fc_rv;
}

static PyObject * fc_PLy_result_str(PyObject *fc_arg)
{
	PLyResultObject *fc_ob = (PLyResultObject *) fc_arg;

	return PyUnicode_FromFormat("<%s status=%S nrows=%S rows=%S>",
								Py_TYPE(fc_ob)->tp_name,
								fc_ob->status,
								fc_ob->nrows,
								fc_ob->rows);
}

static PyObject * fc_PLy_result_subscript(PyObject *fc_arg, PyObject *fc_item)
{
	PLyResultObject *fc_ob = (PLyResultObject *) fc_arg;

	return PyObject_GetItem(fc_ob->rows, fc_item);
}

static int fc_PLy_result_ass_subscript(PyObject *fc_arg, PyObject *fc_item, PyObject *fc_value)
{
	PLyResultObject *fc_ob = (PLyResultObject *) fc_arg;

	return PyObject_SetItem(fc_ob->rows, fc_item, fc_value);
}
